﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 启用状态机的代码组件
/// </summary>
public abstract class StateMachineComponent<T>:MonoBehaviour
{
    // 使用具体状态类的类型作为状态值
    protected internal T state;

    // 在没有匹配到任何注册的状态类时,执行的缺省状态
    protected internal IStateAction<T> defaultStateAction;
    // 状态机对象
    protected internal BasieStateMachine<T> stateMachine=new();

    /// <summary>
    /// 在Uinity的Update事件中调用状态机的Update事件
    /// </summary>
    protected virtual void Update()
    {
        stateMachine.InvokeState(state, out state, defaultStateAction);
    }

    /// <summary>
    /// 在Uinity的FixedUpdate事件中调用状态机的FixedUpdate事件
    /// </summary>
    protected virtual void FixedUpdate()
    {
        stateMachine.InvokeState(state, out state, defaultStateAction, true);
    }
}

/// <summary>
/// 简单状态机
/// </summary>
/// <typeparam name="T">状态类型，建议使用Enum或System.Type作为具体类型</typeparam>
public class BasieStateMachine<T>
{
    // 存放已注册的StateAction
    private readonly Dictionary<T, IStateAction<T>> dictState = new();

    //记录上一个状态，用以识别状态切换
    private T lastState = default;

    /// <summary>
    /// 注册多个StateAction
    /// </summary>
    /// <param name="enumStateAction">状态</param>
    public void Registe(IEnumerable<IStateAction<T>> enumStateAction)
    {
        if (enumStateAction != null)
        {
            enumStateAction.Foreach((IStateAction<T> action) =>
            {
                Registe(action);
            });
        }
    }

    /// <summary>
    /// 注册一个StateAction
    /// </summary>
    /// <param name="stateAction">状态</param>
    public void Registe(IStateAction<T> stateAction)
    {
        T key = stateAction.State;
        if (!dictState.ContainsKey(key))
        {
            dictState.Add(key, stateAction);
        }
        else
        {
            dictState[key] = stateAction;
        }
    }

    public bool TryGetState(T key,out IStateAction<T> stateAction){
        bool ret=false;
        stateAction=null;
        if(key!=null){
            ret=dictState.TryGetValue(key,out stateAction);
        }
        return ret;
    }

    /// <summary>
    /// 注销一个StateAction
    /// </summary>
    /// <param name="stateAction">状态</param>
    public void UnRegiste(IStateAction<T> stateAction)
    {
        T key = stateAction.State;
        if (dictState.ContainsKey(key) && dictState[key] == stateAction)
        {
            dictState.Remove(key);
        }
    }

    /// <summary>
    /// 调用状态的Update函数
    /// </summary>
    /// <param name="inState">输入的状态</param>
    /// <param name="outState">输出的状态</param>
    /// <param name="callFixedUpdate">true表示调用FixedUpdate函数</param>
    /// <returns></returns>
    private bool InvokeStateUpdate(T inState, out T outState, bool callFixedUpdate)
    {
        bool ret = false;
        outState = inState;
        if (NoAction())
        {
            Debug.Log("No any action registed!");
        }
        if (dictState.TryGetValue(inState, out IStateAction<T> action))
        {
            if (callFixedUpdate)
            {
                action.FixedUpdate(inState, out outState);
            }
            else
            {
                action.Update(inState, out outState);
            }
            ret = true;
        }
        return ret;
    }

    /// <summary>
    /// 调用状态的Exit函数
    /// </summary>
    /// <param name="currentState">转换前的状态</param>
    /// <param name="nextState">转换后的状态</param>    
    private bool InvokeStateExit(T currentState, T nextState)
    {
        bool ret = false;
        if (NoAction())
        {
            Debug.Log("No any action registed!");
        }
        if (dictState.TryGetValue(currentState, out IStateAction<T> action))
        {
            action.Exit(currentState, nextState);
            ret = true;
        }
        return ret;
    }
    private bool InvokeStateEnter(T lastState, T currentState)
    {
        bool ret = false;
        if (NoAction())
        {
            Debug.Log("No any action registed!");
        }
        if (dictState.TryGetValue(currentState, out IStateAction<T> action))
        {
            action.Enter(lastState, currentState);
            ret = true;
        }
        return ret;
    }

    /// <summary>
    /// 判断状态机是否没有任何注册函数
    /// </summary>
    /// <returns>没有任何注册函数则返回true</returns>
    private bool NoAction()
    {
        bool noAction = dictState.Count == 0;
        if (noAction)
        {
            bool found = false;
            foreach (IStateAction<T> sa in dictState.Values)
            {
                if (sa != null)
                {
                    found = true;
                    break;
                }
            }
            noAction = !found;
        }
        return noAction;
    }

    /// <summary>
    /// 外部执行状态机的入口方法
    /// </summary>
    /// <param name="inState">输入的状态</param>
    /// <param name="outState">输出的状态</param>
    /// <param name="defaultStateAction">没有匹配的注册状态时执行的缺省状态</param>
    /// <param name="callFixedUpdate">false:执行状态的Update事件, true:执行状态的FixedUpdate事件</param>
    public void InvokeState(T inState, out T outState, IStateAction<T> defaultStateAction = null, bool callFixedUpdate = false)
    {
        outState=inState;
        bool innerStateChanged = (inState != null && lastState != null) && !inState.Equals(lastState);
        if (innerStateChanged)
        {
            if ((inState==null || !this.InvokeStateExit(lastState, inState)) && defaultStateAction != null)
            {
                defaultStateAction.Exit(lastState, inState);
            }
            if ((inState==null || !this.InvokeStateEnter(lastState, inState)) && defaultStateAction != null)
            {
                defaultStateAction.Enter(lastState, inState);
            }
        }
        lastState = inState;
        if ((inState==null || !this.InvokeStateUpdate(inState, out outState, callFixedUpdate)) && defaultStateAction != null)
        {
            //如果没有找到匹配的状态行为，则执行默认的行为
            if (callFixedUpdate)
            {
                defaultStateAction.FixedUpdate(inState, out outState);
            }
            else
            {
                defaultStateAction.Update(inState, out outState);
            }
        }
    }
}

public interface IStateAction<T>
{
    /// <summary>
    /// 进入状态事件
    /// </summary>
    /// <param name="lastState">上一个状态</param>
    /// <param name="currentState">当前状态</param>
    public void Enter(T lastState, T currentState);

    /// <summary>
    /// 退出状态事件
    /// </summary>
    /// <param name="currentState">当前状态</param>
    /// <param name="nextState">下一个状态</param>
    public void Exit(T currentState, T nextState);

    /// <summary>
    /// 在Unity的Update函数中调用此事件
    /// </summary>
    /// <param name="inState">输入的状态</param>
    /// <param name="outState">输出的状态</param>
    public void Update(T inState, out T outState);

    /// <summary>
    /// 在Unity的FixedUpdate函数中调用此事件
    /// </summary>
    /// <param name="inState">输入的状态</param>
    /// <param name="outState">输出的状态</param>
    public void FixedUpdate(T inState, out T outState);

    /// <summary>
    /// 状态类对应的具体的状态值
    /// </summary>
    /// <value>建议使用Enum或System.Type作为具体类型</value>
    public T State { get; }
}

public class GenericStateAction : IStateAction<System.Type>
{
    public delegate void StateEnterAction(System.Type lastState, System.Type currentState);
    /// <summary>
    /// 状态执行方法，将Enter与Update合并在一个方法
    /// </summary>
    /// <param name="enter">true表示状态切换后首次进入函数</param>
    /// <param name="inState">函数输入状态</param>
    /// <param name="outState">函数输出状态</param>
    public delegate void StateUpdateAction(System.Type inState, out System.Type outState);

    /// <summary>
    /// 当状态结束转换为另一个状态时调用
    /// </summary>
    /// <param name="currentState">转换前的状态</param>
    /// <param name="nextState">转换后的状态</param>    
    public delegate void StateExitAction(System.Type currentState, System.Type nextState);    
    public StateEnterAction enterActions;
    public StateUpdateAction updateActions;
    public StateUpdateAction fixedUpdateActions;
    public StateExitAction exitActions;
    public System.Type State { get { return this.GetType(); } }
    public StateMachineComponent<System.Type> stateMachine;

    public GenericStateAction() { }

    public GenericStateAction(StateMachineComponent<System.Type> stateMachine)
    {
        this.stateMachine = stateMachine;
    }

    public void AddEnterDelegate(StateEnterAction enterAction)
    {
        if (enterAction != null)
        {
            enterActions += enterActions;
        }
    }
    public void AddUpdateDelefate(StateUpdateAction updateAction)
    {
        if (updateAction != null)
        {
            updateActions += updateActions;
        }
    }
    public void AddExitDelefate(StateExitAction exitAction)
    {
        if (exitAction != null)
        {
            exitActions += exitActions;
        }
    }

    public virtual void Enter(System.Type lastState, System.Type currentState)
    {
        if (enterActions != null)
        {
            enterActions.Invoke(lastState, currentState);
        }
    }

    public virtual void Exit(System.Type currentState, System.Type nextState)
    {
        if (exitActions != null)
        {
            exitActions.Invoke(currentState, nextState);
        }
    }

    public virtual void Update(System.Type inState, out System.Type outState)
    {
        outState = inState;
        if (updateActions != null)
        {
            updateActions.Invoke(inState, out outState);
        }
    }
    public virtual void FixedUpdate(System.Type inState, out System.Type outState)
    {
        outState = inState;
        if (fixedUpdateActions != null)
        {
            fixedUpdateActions.Invoke(inState, out outState);
        }
    }


}